///\todo upgrade

//////////////////////////////////////////////////////////////////////////////////
//                                                                              //
//  This file is part of the buola project (https://code.google.com/p/buola/).  //
//                                                                              //
//  Copyright(c) 2007-2012 Xavi Gratal                                          //
//  gratal AT gmail DOT com                                                     //
//                                                                              //
//  Buola is free software: you can redistribute it and/or modify               //
//  it under the terms of the GNU General Public License as published by        //
//  the Free Software Foundation, either version 3 of the License, or           //
//  (at your option) any later version.                                         //
//                                                                              //
//  Buola is distributed in the hope that it will be useful,                    //
//  but WITHOUT ANY WARRANTY; without even the implied warranty of              //
//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the               //
//  GNU General Public License for more details.                                //
//                                                                              //
//  You should have received a copy of the GNU General Public License           //
//  along with buola.  If not, see <http://www.gnu.org/licenses/>.              //
//                                                                              //
//////////////////////////////////////////////////////////////////////////////////

#ifndef _BUOLA_IMAGE_CANYIMAGE_H_
#define _BUOLA_IMAGE_CANYIMAGE_H_

#include <buola/image/canyview.h>

namespace buola { namespace img {

class CAnyImage : public CAnyView
{
public:
    CAnyImage()
        :   mBuffer(nullptr)
    {}

    template<typename tFmt>
    CAnyImage(CImage<tFmt> &&pRH)
        :   CAnyView(pRH)
        ,   mBuffer(pRH.mBuffer)
    {
        pRH.mBuffer=nullptr;
    }

    CAnyImage(const CAnyImage &pRH)=delete;

    CAnyImage(CAnyImage &&pRH)
        :   CAnyView(pRH)
        ,   mBuffer(pRH.mBuffer)
    {
        pRH.mBuffer=nullptr;
    }

    ~CAnyImage()
    {
        if(mBuffer)
            DropBuffer();
    }

    CVirtualAllocator *GetAllocator() const     {   return mBuffer?mBuffer->mAlloc:nullptr;        }
    const CCalendarTime &TimeStamp() const  {   return mBuffer->mTimeStamp;     }
    CCalendarTime &TimeStamp()              {   return mBuffer->mTimeStamp;     }
    void TimeStampNow()             {   mBuffer->mTimeStamp=CCalendarClock::now(); }

    void Convert(EImageFormat pFormat);
    
    template<typename tFmt>
    CAnyImage &operator=(CImage<tFmt> &&pRH)
    {
        CAnyView::operator=(pRH);
        if(pRH.mBuffer!=mBuffer)
            std::swap(mBuffer,pRH.mBuffer);
        return *this;
    }

    CAnyImage &operator=(const CAnyImage &pRH)=delete;

    CAnyImage &operator=(CAnyImage &&pRH)
    {
        CAnyView::operator=(pRH);
        if(pRH.mBuffer!=mBuffer)
            std::swap(mBuffer,pRH.mBuffer);
        return *this;
    }

private:
    void DropBuffer()
    {
        //TODO:use some kind of locking
        if(--mBuffer->mRefCount==0)
        {
            if(mBuffer->mAlloc)
                mBuffer->mAlloc->Free(mBuffer->mData);
            else
                free(mBuffer->mData);
            delete mBuffer;
        }

        mBuffer=nullptr;
    }

private:
    detail::CImageBuffer *mBuffer;

    template<typename tFormat>
    friend class CImage;
};

template<typename tFormat>
inline CImage<tFormat>::CImage(CAnyImage &&pRH)
    :   CMView<tFormat>(pRH.MView<tFormat>())
    ,   mBuffer(pRH.mBuffer)
{
    pRH.mBuffer=nullptr;
}

template<typename tFormat,int vPixelAdvance>
inline CView<tFormat,vPixelAdvance>::CView(const CAnyImage &pRH)
        :   mData(const_cast<uint8_t*>(pRH.RawBytes()))
        ,   mSize(pRH.Size())
        ,   mStride(pRH.Stride())
{
    static_assert(vPixelAdvance==1,"only views with vPixelAdvance==1 can be initialized from a CAnyImage");
}

template<typename tFormat,int vPixelAdvance>
inline CMView<tFormat,vPixelAdvance>::CMView(CAnyImage &pRH)
        :   TBase((TPointer)pRH.RawBytes(),pRH.Size(),pRH.Stride())
{
    static_assert(vPixelAdvance==1,"only views with vPixelAdvance==1 can be initialized from a CAnyImage");
}

/*namespace img*/ } /*namespace buola*/ }

#endif
